﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MessageWorkflow.Graph
{
    public static class GraphExtensions
    {
        public static void Add<T>(this IGraph<T> graph, T node, IEnumerable<T> predecessors)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            graph.Add(node);
            graph.AddPredecessors(node, predecessors);
        }

        public static IEnumerable<T> AsOrdered<T>(this IGraph<T> graph)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            Algorithm.OderlyNodeVisitor<T> orderlyNodeVisitor = new Algorithm.OderlyNodeVisitor<T>(graph);
            return orderlyNodeVisitor.Visit();
        }

        public static IEnumerable<T> GetSuccessors<T>(this IGraph<T> graph, T node)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            return from otherNode in graph.Nodes
                   let otherNodeDependencies = graph.GetPredecessors(otherNode)
                   where otherNodeDependencies.Contains(node)
                   select otherNode;
        }

        /// <summary>
        /// Get all nodes within the graph that have no predeccessors
        /// </summary>
        public static IEnumerable<T> GetRootNodes<T>(this IGraph<T> graph)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            return from n in graph.Nodes
                   where !graph.GetPredecessors(n).Any()
                   select n;
        }

        /// <summary>
        /// Get all successors of the value specified including all layers deep
        /// </summary>
        public static IEnumerable<T> GetSuccessorsFlattened<T>(this IGraph<T> graph, T node)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            var result = from successor in graph.GetSuccessors(node)
                         from flattenedSuccessor in graph.GetCurrentAndSuccessorsFlattened(successor)
                         select flattenedSuccessor;

            result = result.Distinct();

            return result;
        }

        /// <summary>
        /// Get the given value and all its successors including all layers deep
        /// </summary>
        public static IEnumerable<T> GetCurrentAndSuccessorsFlattened<T>(this IGraph<T> graph, T node)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            yield return node;

            foreach (T successor in graph.GetSuccessors(node))
            {
                foreach (T flattenedSuccessor in graph.GetCurrentAndSuccessorsFlattened(successor))
                {
                    yield return flattenedSuccessor;
                }
            }
        }

        /// <summary>
        /// Get all Predecessorss of the value specified including all layers up
        /// </summary>
        public static IEnumerable<T> GetPredecessorsFlattened<T>(this IGraph<T> graph, T node)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            var result = from Predecessors in graph.GetPredecessors(node)
                         from flattenedPredecessors in graph.GetCurrentAndPredecessorsFlattened(Predecessors)
                         select flattenedPredecessors;

            result = result.Distinct();

            return result;
        }

        /// <summary>
        /// Get the given value and all its Predecessorss including all layers up    
        /// </summary>
        public static IEnumerable<T> GetCurrentAndPredecessorsFlattened<T>(this IGraph<T> graph, T node)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            yield return node;

            foreach (T Predecessors in graph.GetPredecessors(node))
            {
                foreach (T flattenedPredecessors in graph.GetCurrentAndPredecessorsFlattened(Predecessors))
                {
                    yield return flattenedPredecessors;
                }
            }
        }

        /// <summary>
        /// Builds a graph where each value from the specified value set is included
        /// </summary>
        public static IGraph<T> BuildSubGraph<T>(this IGraph<T> graph, IEnumerable<T> nodes)
        {
            if (graph == null)
                throw new ArgumentNullException("graph");

            Algorithm.SubGraphAlgorithm<T> treeBuilder = new Algorithm.SubGraphAlgorithm<T>(graph);
            return treeBuilder.BuildSubGraph(nodes);
        }
    }
}
